//
// C++ Implementation: memory
//
// Description: Dump memory.
//
//
// Author: Andrey Sharoyko <vanyamboe@gmail.com>, (C) 2011
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include <avr/eeprom.h>
#include "dumpdebug.h"
#include "memory.h"
#include "printpgm.h"

prog_char nMemoryDump[] PROGMEM =
{ "\r\nMemory dump: " };
prog_char ram[] PROGMEM =
{ "RAM" };
prog_char flash[] PROGMEM =
{ "FLASH" };
prog_char eeprom[] PROGMEM =
{ "EEPROM" };
extern prog_char ss[] PROGMEM;

PROGMEM const char* memType[] =
{ ram, flash, eeprom };

prog_char nAvailable[] PROGMEM =
{ "\r\nAvailable: " };
prog_char bytes[] PROGMEM =
{ " Bytes" };

extern unsigned int __bss_end;
extern unsigned int __heap_start;
extern void *__brkval;

unsigned char getByte(dump_addr_t addr, state_t type)
{
    switch (type)
    {
    case RAM:
        return *((unsigned char*) addr);
    case FLASH:
#if defined(DUMP_ARDUINO)
        return pgm_read_byte_near(addr);
#elif defined(DUMP_MEGA)
        if (addr < 0x10000)
            return pgm_read_byte_near(addr);
        else
            return pgm_read_byte_far(addr);
#else
#   error "TODO: Add board support here."
#endif
    case ROM:
        return eeprom_read_byte((unsigned char*) addr);
    }
    return 0;
}

dump_addr_t getMemoryEnd(state_t type)
{
    switch (type)
    {
    case RAM:
        return RAMEND;
    case FLASH:
        return FLASHEND;
    case ROM:
        return E2END;
    }
    return 0;
}

void dumpMemory(dump_addr_t addr, state_t type)
{
    dump_addr_t p = addr;
    dump_addr_t pp;
    char ch;
    unsigned char nEnd = 16;
    dump_addr_t memEnd = getMemoryEnd(type);
    if(p > memEnd+1) {
        p = memEnd+1-256;
        nEnd = 16;
    }
    else
        while (p + (nEnd << 4) > memEnd+1)
            --nEnd;
    printPgm(nMemoryDump);
    printlnPgm(&(memType[type]));
    for (unsigned char n = 0; n < nEnd; ++n)
    {
        dumpAddr((dump_addr_t) p);
        printPgm(ss);
        pp = p;
        for (unsigned char m = 0; m < 16; ++m)
        {
            dumpHexByte(getByte(p, type));
            Serial.print(' ');
            ++p;
        }
        Serial.print(' ');
        for (unsigned char m = 0; m < 16; ++m)
        {
            ch = (char) getByte(pp, type);
            Serial.print((ch >= ' ' && ch < 127) ? ch : '.');
            ++pp;
        }
        Serial.println();
    }
}

void availableMemory()
{
    int free_memory;
    printPgm(nAvailable);

    if ((int) __brkval == 0)
        free_memory = ((int) &free_memory) - ((int) &__bss_end);
    else
        free_memory = ((int) &free_memory) - ((int) __brkval);

    Serial.print(free_memory);
    printlnPgm(bytes);
}
